﻿// *---------------------------------------------------------------------------------------------------------*
// Solution:         JwikIDE									
// Project:           JwiKIDE								
// Source File:    CodeTranslator.cs									
// *---------------------------------------------------------------------------------------------------------*
// 
// © 2012 Digital Six Labs, LLC All Rights Reserved
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the "Software"), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all copies or substantial 
// portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// 
// 
// 
// 
namespace JwiKIDE.JavaClassFileClasses
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class CodeTranslator
    {
        private static int[] ParameterBytes = new[]
            {
                // 00  01  02  03  04  05  06  07  08  09  0a  0b  0c  0d  0e  0f
                0, -1, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, -1, -1, // 00
                1, 2, 1, -1, -1, 1, -1, 1, -1, 1, 0, 0, 0, 0, -1, -1, // 10
                -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, -1, // 20
                0, -1, 0, 0, -1, -1, 1, -1, 1, -1, -1, 0, 0, 0, 0, -1, // 30

                -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, // 40
                -1, 0, -1, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50
                0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, // 60
                0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, // 70

                0, -1, 0, -1, 2, -1, 0, -1, -1, -1, -1, 0, -1, -1, -1, -1, // 80
                -1, -1, 0, -1, -1, 0, 0, -1, -1, 2, 2, 2, 2, 2, 2, 2, // 90
                2, 2, 2, 2, 2, -1, -1, 2, -1, -1, 0, 0, 0, -1, 0, -1, // a0
                -1, 0, 2, 2, 2, 2, 2, 2, 2, -1, -1, 2, 1, 2, 0, -1, // b0

                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // c0
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // d0
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // e0
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // f0
            };

        private const byte OP_NOP = 0x00;

        private const byte OP_ACONST_NULL = 0x01;

        private const byte OP_ICONST_0 = 0x03;

        private const byte OP_SIPUSH = 0x11;

        private const byte OP_LDC = 0x12;

        private const byte OP_ILOAD = 0x15;

        private const byte OP_ALOAD = 0x19;

        private const byte OP_ILOAD_0 = 0x1a;

        private const byte OP_ILOAD_1 = 0x1b;

        private const byte OP_ILOAD_2 = 0x1c;

        private const byte OP_ILOAD_3 = 0x1d;

        private const byte OP_ALOAD_0 = 0x2a;

        private const byte OP_ALOAD_1 = 0x2b;

        private const byte OP_ALOAD_2 = 0x2c;

        private const byte OP_ALOAD_3 = 0x2d;

        private const byte OP_ISTORE = 0x36;

        private const byte OP_ASTORE = 0x3a;

        private const byte OP_ISTORE_0 = 0x3b;

        private const byte OP_ISTORE_1 = 0x3c;

        private const byte OP_ISTORE_2 = 0x3d;

        private const byte OP_ISTORE_3 = 0x3e;

        private const byte OP_ASTORE_0 = 0x4b;

        private const byte OP_ASTORE_1 = 0x4c;

        private const byte OP_ASTORE_2 = 0x4d;

        private const byte OP_ASTORE_3 = 0x4e;

        private const byte OP_I2B = 0x91;

        private const byte OP_I2C = 0x92;

        private const byte OP_I2S = 0x93;

        private const byte OP_TABLESWITCH = 0xaa;

        private const byte OP_LOOKUPSWITCH = 0xab;

        private const byte OP_IRETURN = 0xac;

        private const byte OP_ARETURN = 0xb0;

        private const byte OP_GETSTATIC = 0xb2;

        private const byte OP_PUTSTATIC = 0xb3;

        private const byte OP_GETFIELD = 0xb4;

        private const byte OP_PUTFIELD = 0xb5;

        private const byte OP_INVOKEVIRTUAL = 0xb6;

        private const byte OP_INVOKESPECIAL = 0xb7;

        private const byte OP_INVOKESTATIC = 0xb8;

        private const byte OP_NEW = 0xbb;

        private const byte OP_IFEQ = 0x99;

        private const byte OP_IFNE = 0x9a;

        private const byte OP_IFNULL = 0xc6;

        private const byte OP_IFNONNULL = 0xc7;

        private const byte OP_FCONST_0 = 0x0b; // only if floating pobyte compiled in

        private const byte OP_FCONST_1 = 0x0c; // only if floating pobyte compiled in

        private const byte OP_FCONST_2 = 0x0d; // only if floating pobyte compiled in

        private const byte OP_FLOAD = 0x17; // only if floating pobyte compiled in

        private const byte OP_FLOAD_0 = 0x22; // only if floating pobyte compiled in

        private const byte OP_FLOAD_1 = 0x23; // only if floating pobyte compiled in

        private const byte OP_FLOAD_2 = 0x24; // only if floating pobyte compiled in

        private const byte OP_FLOAD_3 = 0x25; // only if floating pobyte compiled in

        private const byte OP_IALOAD = 0x2e; // only if array compiled in

        private const byte OP_FALOAD = 0x30; // only if array and floating pobyte compiled in

        private const byte OP_AALOAD = 0x32; // only if array compiled in

        private const byte OP_BALOAD = 0x33; // only if array compiled in

        private const byte OP_FSTORE = 0x38; // only if floating pobyte compiled in

        private const byte OP_FSTORE_0 = 0x43; // only if floating pobyte compiled in

        private const byte OP_FSTORE_1 = 0x44; // only if floating pobyte compiled in

        private const byte OP_FSTORE_2 = 0x45; // only if floating pobyte compiled in

        private const byte OP_FSTORE_3 = 0x46; // only if floating pobyte compiled in

        private const byte OP_IASTORE = 0x4f; // only if array compiled in

        private const byte OP_FASTORE = 0x51; // only if array and floating pobyte compiled in

        private const byte OP_AASTORE = 0x53; // only if array compiled in

        private const byte OP_BASTORE = 0x54; // only if array compiled in

        private const byte OP_DUP_X1 = 0x5a; // only if extended stack ops compiled in

        private const byte OP_DUP_X2 = 0x5b; // only if extended stack ops compiled in

        private const byte OP_DUP2_X1 = 0x5d; // only if extended stack ops compiled in

        private const byte OP_DUP2_X2 = 0x5e; // only if extended stack ops compiled in

        private const byte OP_SWAP = 0x5f; // only if extended stack ops compiled in

        private const byte OP_FADD = 0x62; // only if floating pobyte compiled in

        private const byte OP_FSUB = 0x66; // only if floating pobyte compiled in

        private const byte OP_FMUL = 0x6a; // only if floating pobyte compiled in

        private const byte OP_FDIV = 0x6e; // only if floating pobyte compiled in

        private const byte OP_FREM = 0x72; // only if floating pobyte compiled in

        private const byte OP_FNEG = 0x76; // only if floating pobyte compiled in

        private const byte OP_I2F = 0x86; // only if floating pobyte compiled in

        private const byte OP_F2I = 0x8b; // only if floating pobyte compiled in

        private const byte OP_FCMPL = 0x95; // only if floating pobyte compiled in

        private const byte OP_FCMPG = 0x96; // only if floating pobyte compiled in

        private const byte OP_FRETURN = 0xae; // only if floating pobyte compiled in

        private const byte OP_NEWARRAY = 0xbc; // only if array compiled in

        private const byte OP_ANEWARRAY = 0xbd; // only if array compiled in

        private const byte OP_ARRAYLENGTH = 0xbe; // only if array compiled in

        static int get32(byte[] code, int i)
        {
            int val =
                    0x00000001 * code[i + 3]
                  + 0x00000100 * code[i + 2]
                  + 0x00010000 * code[i + 1]
                  + 0x01000000 * code[i + 0];
            return val;
        }

        static void set32(byte[] code, int i, int val)
        {
            code[i + 3] = (byte)(val >> 0);
            code[i + 2] = (byte)(val >> 8);
            code[i + 1] = (byte)(val >> 16);
            code[i + 0] = (byte)(val >> 24);
        }
        public static void Translate(ClassInfo classInfo, byte[] code)
        {
            // process all code bytes
            for (int i = 0; i < code.Length; i++)
            {
                byte cmd = code[i]; //un(byte)(code[i]);

                // code translations to reduce number of instructions
                if (cmd == OP_ASTORE) cmd = OP_ISTORE;
                if (cmd == OP_ASTORE_0) cmd = OP_ISTORE_0;
                if (cmd == OP_ASTORE_1) cmd = OP_ISTORE_1;
                if (cmd == OP_ASTORE_2) cmd = OP_ISTORE_2;
                if (cmd == OP_ASTORE_3) cmd = OP_ISTORE_3;
                if (cmd == OP_ACONST_NULL) cmd = OP_ICONST_0;
                if (cmd == OP_ALOAD) cmd = OP_ILOAD;
                if (cmd == OP_ALOAD_0) cmd = OP_ILOAD_0;
                if (cmd == OP_ALOAD_1) cmd = OP_ILOAD_1;
                if (cmd == OP_ALOAD_2) cmd = OP_ILOAD_2;
                if (cmd == OP_ALOAD_3) cmd = OP_ILOAD_3;

                if (cmd == OP_IFNULL) cmd = OP_IFEQ;
                if (cmd == OP_IFNONNULL) cmd = OP_IFNE;
                if (cmd == OP_ARETURN) cmd = OP_IRETURN;

                // we don't need these conversions, since ints, bytes and
                // shorts are the same internal type
                if (cmd == OP_I2B) cmd = OP_NOP;
                if (cmd == OP_I2C) cmd = OP_NOP;
                if (cmd == OP_I2S) cmd = OP_NOP;




                code[i] = cmd; //(byte)(cmd);



                if (ParameterBytes[cmd] < 0)
                {
                    throw new IOException("Illegal byte code");
                }

                if (cmd == OP_LDC)
                {
                    // load from constant pool (e.g. strings)
                    int index = code[i+1];//un(byte)(code[i + 1]);
                    code[i + 1] = (byte)classInfo.ConstantPool.ConstantRelocate(index);
                }

                if ((cmd == OP_GETFIELD) || (cmd == OP_PUTFIELD))
                {
                    int index = 256 * code[i + 1] + code[i + 2];
                    index = classInfo.ConstantPool.ConstantRelocate(index);
                    code[i + 1] = (byte)(index >> 8);
                    code[i + 2] = (byte)(index & 0xff);
                }

                if ((cmd == OP_GETSTATIC) || (cmd == OP_PUTSTATIC))
                {
                    int index = 256 * code[i + 1] + code[i + 2];
                    index = classInfo.ConstantPool.ConstantRelocate(index);
                    code[i + 1] = (byte)(index >> 8);
                    code[i + 2] = (byte)(index & 0xff);

                    // getstatic usually uses a reference to the object in question.
                    // if the object is native, then this is just an id, that is to
                    // be directly pushed onto the stack, thus we replace the getstatic
                    // instruction with a push instruction
                    if ((cmd == OP_GETSTATIC) && ((index >> 8) >= ConfigFile.LowestNativeId)) code[i] = OP_SIPUSH;
                }

                if ((cmd == OP_INVOKEVIRTUAL) || (cmd == OP_INVOKESPECIAL) || (cmd == OP_INVOKESTATIC))
                {
                    int index = 256 * code[i + 1]+code[i + 2];
                    index = classInfo.ConstantPool.ConstantRelocate(index);
                    code[i + 1] = (byte)(index >> 8);
                    code[i + 2] = (byte)(index & 0xff);
                }

                if (cmd == OP_NEW)
                {
                    int index = 256 * code[i + 1]+code[i + 2];
                    index = classInfo.ConstantPool.ConstantRelocate(index);
                    code[i + 1] = (byte)(index >> 8);
                    code[i + 2] = (byte)(index & 0xff);
                }

                if (cmd == OP_TABLESWITCH)
                {
                    UsedFeatures.Add(FeatureFlags.TABLESWITCH);
                    //System.out.println("tableswitch");
                    // opcode is followed by up to 3 padding bytes (replaced by nop's)
                    // 4 byte low value follows
                    // 4 byte hi value follows
                    // (hi-lo+1) * 4 bytes build the table
                    int delta = 0;
                    i++;
                    while (i % 4 != 0)
                    {
                        code[i - 1] = OP_NOP;
                        code[i] = OP_LOOKUPSWITCH;
                        i++;
                        delta++;
                    }
                    set32(code, i, get32(code, i) - delta); // realloc default label
                    i += 4;
                    int lo = get32(code, i + 0);
                    int hi = get32(code, i + 4);
                    i += 8;
                    for (int j = 0; j < (hi - lo + 1); j++)
                    {
                        set32(code, i, get32(code, i) - delta); // realloc label
                        i += 4;
                    }
                    i--;
                }

                if (cmd == OP_LOOKUPSWITCH)
                {
                    UsedFeatures.Add(FeatureFlags.LOOKUPSWITCH);
                    //System.out.println("lookupswitch");
                    // opcode is followed by up to 3 padding bytes
                    // 4 byte default offset follows
                    // 4 byte count follows
                    // count * 2 * 4 bytes build the table
                    int delta = 0;
                    i++;
                    while (i % 4 != 0)
                    {
                        code[i - 1] = (byte)(OP_NOP);
                        code[i] = (byte)(OP_LOOKUPSWITCH);
                        i++;
                        delta++;
                    }
                    set32(code, i, get32(code, i) - delta); // realloc default label
                    i += 4;
                    int count = get32(code, i);
                    i += 4;
                    for (int j = 0; j < count; j++)
                    {
                        i += 4; // skip value
                        set32(code, i, get32(code, i) - delta); // realloc label
                        i += 4;
                    }
                    i--;
                }




                // update used bits
                if (cmd == OP_TABLESWITCH) UsedFeatures.Add(FeatureFlags.TABLESWITCH);
                if (cmd == OP_LOOKUPSWITCH) UsedFeatures.Add(FeatureFlags.LOOKUPSWITCH);
                if (cmd == OP_FCONST_0) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FCONST_1) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FCONST_2) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FLOAD) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FLOAD_0) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FLOAD_1) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FLOAD_2) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FLOAD_3) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_IALOAD) UsedFeatures.Add(FeatureFlags.ARRAY);
                if (cmd == OP_FALOAD) UsedFeatures.Add(FeatureFlags.ARRAY | FeatureFlags.FLOAT);
                if (cmd == OP_BALOAD) UsedFeatures.Add(FeatureFlags.ARRAY);
                if (cmd == OP_FSTORE) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FSTORE_0) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FSTORE_1) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FSTORE_2) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FSTORE_3) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_IASTORE) UsedFeatures.Add(FeatureFlags.ARRAY);
                if (cmd == OP_FASTORE) UsedFeatures.Add(FeatureFlags.ARRAY | FeatureFlags.FLOAT);
                if (cmd == OP_BASTORE) UsedFeatures.Add(FeatureFlags.ARRAY);
                if (cmd == OP_FADD) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FSUB) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FMUL) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FDIV) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FREM) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FNEG) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_I2F) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_F2I) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FCMPL) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FCMPG) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_FRETURN) UsedFeatures.Add(FeatureFlags.FLOAT);
                if (cmd == OP_NEWARRAY) UsedFeatures.Add(FeatureFlags.ARRAY);
                if (cmd == OP_ANEWARRAY) UsedFeatures.Add(FeatureFlags.ARRAY);
                if (cmd == OP_ARRAYLENGTH) UsedFeatures.Add(FeatureFlags.ARRAY);
                if (cmd == OP_DUP_X1) UsedFeatures.Add(FeatureFlags.EXTSTACK);
                if (cmd == OP_DUP_X2) UsedFeatures.Add(FeatureFlags.EXTSTACK);
                if (cmd == OP_DUP2_X1) UsedFeatures.Add(FeatureFlags.EXTSTACK);
                if (cmd == OP_DUP2_X2) UsedFeatures.Add(FeatureFlags.EXTSTACK);
                if (cmd == OP_SWAP) UsedFeatures.Add(FeatureFlags.EXTSTACK);
                i += ParameterBytes[cmd];
            }


        }
    }
}
